//-----------------------------------------------------------------------
// <copyright file="Utilities.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <summary>Provides Utility functions to all the tasks</summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks
{
    #region Using directives
    using System;
    using System.Text;
    using System.IO;
    using System.Collections;
    using System.Globalization;
    using System.Resources;
    using System.Reflection;
    using System.Xml;
    using Microsoft.Build.Framework;
    using Microsoft.Build.Utilities;
    using Microsoft.Sdc.Tasks.Configuration.ComponentServices;
    using Microsoft.Sdc.Tasks.Configuration;
    using System.Security;
    #endregion

    class Utilities
    {
        private Task parentTask;
        private ResourceManager resourceManager;

        /// <summary>
        /// Initializes a new instance of the Utilities class.
        /// </summary>
        internal Utilities(Task task, ResourceManager resourceManager)
        {
            this.parentTask = task;
            this.resourceManager = resourceManager;
        }

        /// <summary>
        /// Prevents a new instance of the Utilities class from being created publicly.
        /// </summary>
        private Utilities()
        {
        }

        /// <summary>
        /// TODO
        /// </summary>
        /// <param name="args">TODO</param>
        /// <param name="message">TODO</param>
        internal void SdCmd(string args, string message)
        {
            ShellExecute shellExecute = new ShellExecute();
            shellExecute.Filename = "sd.exe";
            this.parentTask.Log.LogMessage(message);
            shellExecute.Arguments = args;
            shellExecute.Execute();
            this.parentTask.Log.LogMessage(MessageImportance.Low, shellExecute.StandardOutput);
            this.parentTask.Log.LogMessage(shellExecute.StandardError);
        }

        internal void UpdateApplication(string applicationName, string identity, string password, bool enforceAccessChecks, Activation activation, bool runForever, string shutdownAfter)
        {
            this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "COM+ updating '{0}' ...", applicationName));
            Application application = Application.Load(applicationName);
            if (identity != null && identity.Length > 0)
            {
                if (password == null)
                {
                    password = String.Empty;
                }
                application.Identity = identity;
                application.Password = password;
            }
            application.Activation = activation;
            application.RunForever = runForever;
            if (shutdownAfter != null && shutdownAfter.Length > 0)
            {
                int shutdown = Convert.ToInt32(shutdownAfter, CultureInfo.InvariantCulture);
                application.ShutdownAfter = shutdown;
            }
            application.AccessChecksEnabled = enforceAccessChecks;
            this.parentTask.Log.LogMessage(MessageImportance.Low, String.Format(CultureInfo.InvariantCulture, "About to save Application '{0}' ...", applicationName));
            application.Save();
            this.parentTask.Log.LogMessage(MessageImportance.Low, String.Format(CultureInfo.InvariantCulture, "Application '{0}' saved ...", applicationName));
        }

        internal int CheckWebSiteDescriptionOrIdentifier(string description, string identifier, string action, string machineName)
        {
            int id;
            if ((description != null && description.Length > 0) && (identifier != null && identifier.Length > 0))
            {
                throw new ArgumentException("Specify either description or identifier but not both");
            }

            if ((description == null && description.Length == 0) && (identifier == null && identifier.Length == 0))
            {
                throw new ArgumentException("Specify either description or identifier");
            }

            if (string.IsNullOrEmpty(description) == false)
            {
                this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "{1} website '{0}' on: {2}", description, action, machineName));
                id = Microsoft.Sdc.Tasks.Configuration.Web.WebSite.GetIdentifierFromDescription(machineName, description);
            }
            else
            {
                this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "{1} website '{0}' on {2}", identifier, action, machineName));
                id = Convert.ToInt32(identifier, CultureInfo.InvariantCulture);
            }
            return id;
        }
        /*
        internal int CheckWebSiteDescriptionOrIdentifier(string description, string identifier, string action)
        {
            int id;
            if ((description != null && description.Length > 0) && (identifier != null && identifier.Length > 0))
            {
                throw new ArgumentException("Specify either description or identifier but not both");
            }

            if ((description == null && description.Length == 0) && (identifier == null && identifier.Length == 0))
            {
                throw new ArgumentException("Specify either description or identifier");
            }

            if (string.IsNullOrEmpty(description) == false)
            {
                this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "{1} website '{0}'...", description, action));
                id = Microsoft.Sdc.Tasks.Configuration.Web.WebSite.GetIdentifierFromDescription("localhost", description);
            }
            else
            {
                this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "{1} website '{0}'...", identifier, action));
                id = Convert.ToInt32(identifier, CultureInfo.InvariantCulture);
            }
            return id;
        }*/

        internal int CheckFtpSiteDescriptionOrIdentifier(string description, string identifier, string action)
        {
            int id;
            if ((description != null && description.Length > 0) && (identifier != null && identifier.Length > 0))
            {
                throw new ArgumentException("Specify either description or identifier but not both");
            }
            if ((description == null && description.Length == 0) && (identifier == null && identifier.Length == 0))
            {
                throw new ArgumentException("Specify either description or identifier");
            }

            if (description != null && description.Length > 0)
            {
                this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "{1} ftpsite '{0}'...", description, action));
                id = Microsoft.Sdc.Tasks.Configuration.Web.FtpSite.GetIdentifierFromDescription(description);
            }
            else
            {
                this.parentTask.Log.LogMessage(String.Format(CultureInfo.InvariantCulture, "{1} ftpsite '{0}'...", identifier, action));
                id = Convert.ToInt32(identifier, CultureInfo.InvariantCulture);
            }
            return id;
        }

        //TODO perf issue
        //need to create appdomain and load it in there and then destroy the app domain
        internal bool IsValidAssemblyFile(string path)
        {
            try
            {
                Assembly assembly = Assembly.LoadFrom(path);
                return true;
            }
            catch (BadImageFormatException)
            {
                return false;
            }
        }
                
        /// <summary>
        /// Get localised message for task exceptions
        /// </summary>
        /// <param name="taskException"></param>
        /// <returns></returns>
        internal string CreateExceptionInformation(TaskException taskException)
        {
            return CreateExceptionInformation(taskException, GetMessageFromTaskException(taskException));
        }

        private String GetMessageFromTaskException(TaskException taskException)
        {
            string resourceString = this.resourceManager.GetString(taskException.ResourceName);
            if (resourceString == null)
            {
                this.parentTask.Log.LogError("Could not find resource string " + taskException.ResourceName);
                return taskException.ResourceName;
            }
            if (taskException.Values != null)
            {
                return String.Format(CultureInfo.InvariantCulture,resourceString, taskException.Values);
            }
            else
            {
                return resourceString;
            }
        }

        private string CreateExceptionInformation(Exception exception, string message)
        {
            Type t = this.parentTask.GetType();
            int width = CalculateLongestPropertyName(t);

            StringBuilder summary = new StringBuilder();
            summary.Append("A task error has occured.");
            summary.Append(Environment.NewLine);
            WriteNameValue(summary, "Message".PadRight(width), message, true);
            summary.Append(CreateSummaryInformation(t, width));
            summary.Append(Environment.NewLine);
            summary.Append(exception.StackTrace);
            if (exception.InnerException != null)
            {
                summary.Append(CreateInnerExceptionSummary(exception.InnerException));
            }
            return summary.ToString();
        }

        /// <summary>
        /// Handle general exception
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        internal string CreateExceptionInformation(Exception exception)
        {
            return CreateExceptionInformation(exception, exception.Message);
        }

        private int CalculateLongestPropertyName(Type type)
        {
            //this is the min width
            int maxWidth = 7;

            PropertyInfo[] publicProperties = type.GetProperties();
            foreach (PropertyInfo property in publicProperties)
            {
                // Ignore indexer properties and properties without a get method
                if ((property.GetIndexParameters() == null || property.GetIndexParameters().Length == 0) &&
                    (property.GetGetMethod() != null))
                {
                    if (property.Name != "BuildEngine" &&
                        property.Name != "HostObject" &&
                        property.Name != "Log")
                    {
                        if (property.Name.Length > maxWidth)
                        {
                            maxWidth = property.Name.Length;
                        }
                    }
                }
            }

            return maxWidth;
        }

        private string Pad(string text, int width)
        {
            return text.PadRight(width);
        }

        private void WriteNameValue(StringBuilder stringBuilder, string name, string value, bool writeNewLine)
        {
            stringBuilder.Append(name);
            stringBuilder.Append(" = ");
            stringBuilder.Append(value);
            if (writeNewLine)
            {
                stringBuilder.Append(Environment.NewLine);
            }
        }

        private string GetStringValue(object o)
        {
            string resultText = String.Empty;
            if (o == null)
            {
                resultText = "<null>";
            }
            else
            {
                resultText = o.ToString().Trim();
            }
            if (resultText.Length == 0)
            {
                resultText = "<String.Empty>";
            }
            return resultText;
        }

        private string CreateSummaryInformation(Type type, int width)
        {
            StringBuilder summary = new StringBuilder();
            PropertyInfo[] publicProperties = type.GetProperties();
            foreach (PropertyInfo property in publicProperties)
            {
                try
                {
                    // Ignore indexer properties and properties without a get method
                    if ((property.GetIndexParameters() == null || property.GetIndexParameters().Length == 0) &&
                        (property.GetGetMethod() != null))
                    {

                        Object result = property.GetValue(this.parentTask, null);
                        if (property.Name != "BuildEngine" &&
                                property.Name != "HostObject" &&
                                property.Name != "Log")
                        {

                            // If it's a string array - dump it out
                            if ((result != null) && (result.GetType().IsArray) && (result.GetType().GetElementType() == typeof(string)))
                            {
                                string[] resultArray = (string[]) result;
                                for (int i = 0; i < resultArray.Length; i++)
                                {
                                    string name = property.Name + "[" + Convert.ToString(i, CultureInfo.InvariantCulture) + "]";
                                    WriteNameValue(summary, name.PadRight(width), resultArray[i], true);
                                }
                            }
                            else
                            {
                                WriteNameValue(summary, property.Name.PadRight(width), GetStringValue(result), true);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    this.parentTask.Log.LogErrorFromException(e);
                }
            }
            return summary.ToString();
        }

        private string CreateInnerExceptionSummary(Exception exception)
        {
            return CreateInnerExceptionSummary(exception, exception.Message);
        }

        private string CreateInnerExceptionSummary(Exception innerException, string message)
        {
            StringBuilder summary = new StringBuilder();
            summary.Append(Environment.NewLine);
            summary.Append(message);
            summary.Append(Environment.NewLine);
            summary.Append(innerException.StackTrace);
            if (innerException.InnerException != null)
            {
                summary.Append(CreateInnerExceptionSummary(innerException.InnerException));
            }
            return summary.ToString();
        }

        internal void RethrowUnlessFileIO(Exception e)
        {
            if (((!(e is UnauthorizedAccessException) && !(e is ArgumentNullException)) && (!(e is PathTooLongException) && !(e is DirectoryNotFoundException))) && ((!(e is NotSupportedException) && !(e is ArgumentException)) && (!(e is SecurityException) && !(e is IOException))))
            {
                throw e;
            }
        }
 

    }
}
